<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body{
            padding-bottom: 800px;
            padding-left: 30px;
        }
        .cbox{
            border: 1px solid #999;
            padding: 20px;
            margin: 10px 0px;
        }
    </style>
</head>
<body>
    <div id="app">
        <h4>子向父</h4>
        <ul>
            <li>传递技术：事件绑定，事件触发</li>
            <li>事件绑定：<br>
                1. 在父组件容器中为子组件标签完成自定义事件绑定操作【 v-on:自定义事件名=父组件方法仓库的方法 】<br>
                2. vue会完成组件实例构建操作，在createVNode创建虚拟DOM时，绑定在子组件标签上的所有vue语法都会被记录下来 <br>
                3. 其中以v-on 绑定的事件会被定义在子组件实例对应的实例属性 $attrs 中，以自定义事件回调方法方式存在【事件名会被转换为驼峰规则，并在名称前增加on】 <br>
                4. 当完成 render 函数渲染时，$attrs 中记录的事件会被绑定在子组件根元素上作为普通标签自定义事件存在 <br>
            </li>
            <li>事件拦截触发: <br>
                1. 子组件配置中通过 emits 属性定义，将$attrs中记录的相关事件方法，转存为一个子组件私有事件功能，存储在组件实例的emitsOptions属性中 <br>
                2. vue同时提供一个触发函数 $emit(事件名,事件源数据) ，用于执行当前组件中拦截的事件=>通过该方法在调试工具中产生日志 <br>
                3. 当 emits 完成定义后，$attrs中对应的事件将被删除 <br>
            </li>
        </ul>
        <p>tempMsg:{{ tempMsg }}</p>
        <p>info:{{ info }}</p>
        <!-- v-on指令只有第一个:是参数关键字，后续出现的:都是参数内容的一部分 -->
        <!-- 绑定的事件回调没有定义()的时候，子组件事件触发的参数会作为回调的参数进行传递 -->
        <!-- <child-comp v-on:test="printTest" v-on:update:info="updateInfo"></child-comp> -->
        <!-- 绑定的事件回调定义了()时候，子组件事件触发的参数会作为自定义事件的自定义事件源对象传入【$event】 -->
        <child-comp v-on:test="printTest" v-on:update:info="updateInfo($event)"></child-comp>
    </div>
    <script type="module">
        // 加载 Vue3 的ESM 模块文件
        import { createApp } from "../../assets/vue3/vue.esm-browser.js"
        createApp({
            data(){
                return {
                    tempMsg:"",
                    info:""
                }
            },
            methods: {
                printTest(arg) {
                    console.log("test",arg)
                    this.tempMsg = arg;
                },
                updateInfo(arg){
                    console.log("updateInfo",arg)
                    this.info = arg;
                }
            },
            components: {
                ChildComp:{
                    template:`
                        <div class="cbox">
                            <p>组件ChildComp</p>
                            <p>msg:{{ msg }}</p>
                            <input type="button" value="调用父组件方法" @click="$attrs.onTest()" >
                            <input type="button" value="调用父组件方法+传参数" @click="execParentCallBack()" >
                            <p>info:{{ info }}</p>
                            <input type="button" value="调用父组件方法+传递参数" @click="sendInfo()" >
                        </div>
                    `,
                    // 拦截属性emits取值数组方式进行事件绑定拦截，元素值为绑定时的事件名称
                    emits:["update:info"],
                    data() {
                        return {
                            msg: "子组件数据msg",
                            info:"子组件数据info"
                        }
                    },
                    methods: {
                        execParentCallBack() {
                            this.$attrs.onTest(this.msg)
                        },
                        sendInfo(){
                            console.log(this)
                            // this["update:info"]();
                            this.$emit("update:info",this.msg)
                        }
                    },
                },
            },
        }).mount("#app")
    </script>
</body>
</html>